
import { IndexImage, loadImage } from './index';

class TextureLoader {
  /** @type {TextureSheet[]} */
  sheets = [];
  textureMap = {};
  /** @type {string[]} */
  needToCache = [];

  findSheet(url) {
    return this.sheets.find(s => s.url === url);
  }

  putSheet(url) {
    let sheet = this.findSheet(url);
    if (sheet) return sheet;
    sheet = new TextureSheet(url, this)
    this.sheets.push(sheet);
    return sheet;
  }

  /**
   * 将它所有的 texture 全部缓存
   * @param {string} url
   */
  load(url) {
    let sheet = this.findSheet(url);
    if (!sheet) {
      sheet = this.putSheet(url);
    }
    return sheet.loadAll().then(() => {});
  }

  findTextureDefine(name) {
    /** @type {TextureSheet} */
    let sheet = this.textureMap[name];
    if (!sheet) return null;
    return sheet.findTextureDefine(name);
  }

  /**
   * 让 Loader 预先加载某些 texture
   * @param {string} name
   */
  cache(name) {
    /** @type {TextureSheet} */
    let sheet = this.textureMap[name];
    if (!sheet) {
      this.needToCache.push(name);
      return;
    }
    sheet.get(name);
  }

  get(name) {
    /** @type {TextureSheet} */
    let sheet = this.textureMap[name];
    if (!sheet) return Promise.reject({ msg : `不存在为 ${name} 的 texture`, value : name });

    return sheet.get(name);
  }

  getFromCache(name) {
    /** @type {TextureSheet} */
    let sheet = this.textureMap[name];
    return sheet ? sheet.getFromCache(name) : null;
  }

  /**
   * @param {TextureSheet} sheet 
   */
  _whenTextureLoaded(sheet) {
    const { textureDefs } = sheet;
    textureDefs.forEach(def => this.textureMap[def.name] = sheet);
  }
}

class TextureSheet {
  loading = true;

  imagePath = '';
  /** @type {ImageData} */
  imageData;
  /** @type {{name:string,x:number,y:number,w:number,h:number,offx:number,offy:number}[]} */
  textureDefs = [];
  /**
   * 存储 IndexImage | Promise<IndexImage> 的 map
   */
  textures = {};
  textureLoadedCount = 0;

  /**
   * @param {string} url
   * @param {TextureLoader} parent
   */
  constructor(url, parent) {
    this.url = url;
    this.parent = parent;
    this.loadTask = this.load();
  }

  load() {
    return fetch(this.url)
      .then(d => d.json())
      .then(d => {
        this.imagePath = this.url.substring(0, this.url.lastIndexOf('/')) + '/' + d.image;
        /** @type {AvatarTextureDef[]} */
        this.textureDefs = d.textures.map(t => {
          let def = {};
          Object.assign(def, t);
          if (!def.offx) def.offx = 0;
          if (!def.offy) def.offy = 0;
          return def;
        });
      })
      .then(() => {
        this.parent._whenTextureLoaded(this);
        return loadImage(this.imagePath)
      })
      .then(data => {
        this.imageData = data;
        this.loading = false;
      })
      .catch(e => {
        console.error({ msg : `读取 ${this.url} 数据失败: ${e}` });
        this.loading = false;
      })
  }

  loadAll() {
    return this.load()
      .then(() => {
        return Promise.all(this.textureDefs.map(def => loadToCache(this, def.name)));
      });
  }

  findTextureDefine(name) {
    return this.textureDefs.find(o => o.name === name);
  }

  getFromCache(name) {
    let cache = this.textures[name];
    return (cache instanceof IndexImage) ? cache : null;
  }

  /**
   * @param {string} name
   * @returns {Promise<IndexImage>}
   */
  get(name) {
    const { textures, loading } = this;

    let cache = textures[name];
    if (cache) {
      return (cache instanceof Promise) ? cache.then(() => textures[name]) : Promise.resolve(cache);
    }
    if (loading) {
      return new Promise((resolve, reject) => {
        this.loadTask.then(() => {
          loadToCache(this, name)
            .then(() => resolve(this.textures[name]))
            .catch(e => reject(e));
        });
      });
    }

    return loadToCache(this, name)
      .then(() => textures[name]);
  }

  /**
   * 将所有在 cache 里面的 IndexImage 或 Promise<IndexImage> 返回.
   * 返回成 key-value 对的形式, key = name, sheet 定义的名称
   */
  allFromCache() {
    let o = {};
    Object.assign(o, this.textures);
    return o;
  }
}

/**
 * @param {TextureSheet} sheet
 * @param {string} name
 * @returns {Promise<void>}
 */
function loadToCache(sheet, name) {
  let define = sheet.findTextureDefine(name);
  if (!define) {
    return Promise.reject({ msg : `不存在为 ${name} 的 texture`, value : sheet });
  }

  return sheet.textures[name] = new Promise((resolve) => {
    // 强制让出 cpu 时间
    setTimeout(() => {
      const image = new IndexImage(sheet.imageData, define.x, define.y, define.w, define.h);
      sheet.textures[name] = image;
      sheet.textureLoadedCount++;

      // 如果所有 texture 都加载了, 那 imageData 作为缓存已经没必要存在了, 删了把内存让出来
      if (sheet.textureLoadedCount == sheet.textureDefs.length) {
        sheet.imageData = null;
      }

      resolve(image);
    });
  });
}

export {
  TextureLoader, TextureSheet,
};
